package com.zzg.lucene;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class SearchAll {
	private static List<Passage> psgList = null;

	// 写对象
	private static IndexWriter writer = null;

	// 数据初始化
	static {
		psgList = new ArrayList<Passage>();

		// 产生一堆数据
		psgList.add(new Passage(1, "yellowcong", "717350389@qq.com", "逗比", 23, "I LOVE YOU ", new Date()));

		psgList.add(new Passage(2, "张三", "zhashang@qq.com", "逗比", 23, "三炮", new Date()));
		psgList.add(new Passage(3, "李四", "lisi@neusoft.com", "逗比", 23, "三炮", new Date()));
		psgList.add(new Passage(4, "王五", "wangwu@aliyun.com", "逗比", 23, "三炮", new Date()));
		psgList.add(new Passage(5, "赵六", "zhaoliu@baidu.com", "逗比", 23, "三炮", new Date()));
		System.out.println("-------------------------添加的数据----------------------");
		for (Passage psg : psgList) {
			System.out.println(psg.getId() + ":" + psg.getUsername() + ":" + psg.getEmail() + ":" + psg.getContent());
		}
	}

	// 删除全部索引
	public static void deleteAll() {
		IndexWriter writer = null;
		try {
			// 获取IndexWriter
			writer = getIndexWriter();

			// 删除所有的数据
			writer.deleteAll();

			int cnt = writer.numDocs();
			System.out.println("索引条数\t" + cnt);

			// 提交事物
			writer.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//coloseWriter(writer);
		}
	}

	// 创建索引目录
	private static String getIndexPath() {
		// 获取索引的目录
		String path = "c:\\lucene\\index";

		// 不存在就创建目录
		File file = new File(path);
		if (!file.exists()) {
			file.mkdirs();
		}
		return path;
	}

	// IndexWrite 创建
	public static IndexWriter getIndexWriter() throws Exception {
		// 创建IdnexWriter
		String path = getIndexPath();
		FSDirectory fs = FSDirectory.open(Paths.get(path));
		if(writer == null){
			// 创建IndexWriterConfig 对象
			IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new StandardAnalyzer());
			indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
			// 创建writer对象
			writer = new IndexWriter(fs, indexWriterConfig);			
		}
		return writer;
	}

	// IndexReader 创建
	public static IndexReader getIndexReader() throws Exception {
		// 创建IdnexWriter
		String path = getIndexPath();
		Directory fs = FSDirectory.open(Paths.get(path));
		return DirectoryReader.open(fs);
	}

	// 索引创建
	public static void index() {
		IndexWriter writer = null;
		try {
			// 1、获取IndexWriter
			writer = getIndexWriter();
			

			// 2、建立索引
			for (Passage psg : psgList) {
				Document doc = new Document();

				// IntField 不能直接检索到，需要结合
				doc.add(new IntPoint("id", psg.getId()));

				// 用户String类型的字段的存储，StringField是只索引不分词
				doc.add(new TextField("username", psg.getUsername(), Field.Store.YES));

				// 主要对int类型的字段进行存储，需要注意的是如果需要对InfField进行排序使用SortField.Type.INT来比较，如果进范围查询或过滤，需要采用NumericRangeQuery.newIntRange()
				doc.add(new IntPoint("age", psg.getAge()));

				// 对String类型的字段进行存储，TextField和StringField的不同是TextField既索引又分词
				doc.add(new TextField("content", psg.getContent(), Field.Store.NO));

				doc.add(new StringField("keyword", psg.getKeyword(), Field.Store.YES));

				doc.add(new StringField("email", psg.getEmail(), Field.Store.YES));

				// 日期数据添加索引
				doc.add(new LongPoint("addDate", psg.getAddDate().getTime()));

				// 3、添加文档
				writer.addDocument(doc);
			}

			// 索引条数
			int cnt = writer.numDocs();
			System.out.println("索引条数\t" + cnt);

			// 提交事物
			writer.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			coloseWriter(writer);
		}
	}

	// 关闭 IndexWriter
	private static void coloseWriter(IndexWriter writer) {
		try {
			if (writer != null) {
				writer.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 关闭 IndexReader
	public static void coloseReader(IndexReader reader) {
		try {
			if (reader != null) {
				reader.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws ParseException {
		 // 删除 所有索引
        deleteAll();

        // 建立索引
        index();
        
        QueryParser parser = getQueryParser();
        //设定第一个* 可以匹配
        parser.setAllowLeadingWildcard(true);
        
      //精确String 类型查询
        System.out.println("-------------查询用户名为yellowcong的数据-------------");
        //精确查询，根据名称来直接
        Query query = parser.parse("username:yellowcong");
        //执行查询
        excQuery(query);


        //范围查询
        System.out.println("-------------查询id在1-3的数据-------------");
        //查询前三条数据，后面两个true,表示的是是否包含头和尾
        //匹配区间， 其中TO 必须是大写的，还有有空格
        query = parser.parse("id:[1 TO 3]"); //设定查询的Id为 1-4

        //执行查询
        excQuery(query);


        //前缀匹配查询
        System.out.println("-------------查询前缀 邮箱 以z开头的数据-------------");
        //查询前缀 邮箱 以z开头的数据
        query = parser.parse("email:z*");
        excQuery(query);

        //通配符查询
        //通配符就更简单了，只要知道“*”表示0到多个字符，而使用“？”表示一个字符就行了
        System.out.println("-------------------查询email 以 @qq结尾的数据--------------");
        //查询email 以 @qq结尾的数据
        //需要设定 * 可以放在前面  QueryParser.setAllowLeadingWildcard(true);
        //将字符串放在首位，默认情况下回报错
        query = parser.parse("email:*@qq.com");
        excQuery(query);        


        //短语查询
        System.out.println("------------查询内容中，有I　LOVE　YOU 的数据---------------");
        //短语查询，但是对于中文没有太多的用，其中查询的时候还有
        query =parser.parse("\"i you\"~2"); //表示中间含有一个单词
        excQuery(query);

        //模糊查询
        System.out.println("-------------------------模糊查询---------------");
        query = parser.parse("username:zhangsan~");
        excQuery(query);

	}

	// 创建查询
	private static QueryParser getQueryParser() {
		QueryParser parser = new QueryParser("content", new StandardAnalyzer());
		return parser;
	}

	// 执行查询
	public static void excQuery(Query query) {
		// 查询
		IndexReader reader = null;
		try {
			reader = getIndexReader();

			// 获取查询数据
			IndexSearcher searcher = new IndexSearcher(reader);

			// 检索数据
			TopDocs topDocs = searcher.search(query, 100);
			for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
				// 湖区偶
				Document doc = reader.document(scoreDoc.doc);
				System.out.println(doc.get("id") + ":" + doc.get("username") + ":" + doc.get("email"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			coloseReader(reader);
		}
	}

	static class Passage {
		private int id;
		private String username;
		private String email;
		private String keyword;
		private int age;
		// 这个模拟的是文章
		private String content;
		private Date addDate;

		public Passage(int id, String username, String email, String keyword, int age, String content, Date addDate) {
			super();
			this.id = id;
			this.username = username;
			this.email = email;
			this.keyword = keyword;
			this.age = age;
			this.content = content;
			this.addDate = addDate;
		}

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getUsername() {
			return username;
		}

		public void setUsername(String username) {
			this.username = username;
		}

		public String getEmail() {
			return email;
		}

		public void setEmail(String email) {
			this.email = email;
		}

		public String getKeyword() {
			return keyword;
		}

		public void setKeyword(String keyword) {
			this.keyword = keyword;
		}

		public int getAge() {
			return age;
		}

		public void setAge(int age) {
			this.age = age;
		}

		public String getContent() {
			return content;
		}

		public void setContent(String content) {
			this.content = content;
		}

		public Date getAddDate() {
			return addDate;
		}

		public void setAddDate(Date addDate) {
			this.addDate = addDate;
		}

	}
}
